package win.doyto.service.data.report;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.ibatis.builder.xml.XMLMapperEntityResolver;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.parsing.XPathParser;
import org.apache.ibatis.scripting.xmltags.DynamicContext;
import org.apache.ibatis.scripting.xmltags.DynamicSqlSource;
import org.apache.ibatis.scripting.xmltags.SqlNode;
import org.apache.ibatis.scripting.xmltags.XMLScriptBuilder;
import org.apache.ibatis.session.Configuration;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.mybatis.spring.boot.autoconfigure.MybatisProperties;
import org.springframework.stereotype.Component;

import java.util.Map;
import javax.annotation.Resource;

import static win.doyto.service.data.report.ReportMapper.SQL_KEY;

/**
 * ReportMapperAdvice
 *
 * @author f0rb on 2018-02-07.
 */
@Slf4j
@Aspect
@Component
public class ReportMapperAdvice {

    @Resource
    private MybatisProperties mybatisProperties;

    public static void buildSqlScript(Configuration configuration, Map<String, Object> parameterObject) {
        String sql = (String) parameterObject.get(SQL_KEY);
        if (sql != null && sql.startsWith("<script>")) {
            XPathParser parser = new XPathParser(sql, false, configuration.getVariables(), new XMLMapperEntityResolver());
            XMLScriptBuilder builder = new XMLScriptBuilder(configuration, parser.evalNode("/script"), parameterObject.getClass());

            SqlSource sqlSource = builder.parseScriptNode();
            if (sqlSource instanceof DynamicSqlSource) {
                try {
                    SqlNode rootSqlNode = (SqlNode) FieldUtils.readField(sqlSource, "rootSqlNode", true);
                    DynamicContext context = new DynamicContext(configuration, parameterObject);
                    rootSqlNode.apply(context);
                    log.debug("Origin SQL: {}", sql);
                    sql = context.getSql();
                    log.debug("Actual SQL: {}", sql);
                    parameterObject.put(SQL_KEY, sql);
                } catch (IllegalAccessException e) {
                    log.error("IllegalAccessException", e);
                }
            }
        }
    }

    @Before("execution(* win.doyto.service.data.report.ReportMapper.get*(..)) && args(map)")
    public void build(Map<String, Object> map) {
        buildSqlScript(mybatisProperties.getConfiguration(), map);
    }
}